/*
 *  ckr_spec_ConsoleSpecRunner.cpp
 *  ckrSpec
 *
 *  Created by C. Keith Ray on 2006.11.13.
 *  Copyright 2006 __MyCompanyName__. All rights reserved.
 *
 */

#include "ckr_spec_ConsoleSpecRunner.h"
#include "ckr_spec_SpecListener.h"
#include "ckr_spec_Context.h"
#include "ckr_spec_StringUtils.h"

#include <iostream>
#include <algorithm>

using namespace std;

namespace  ckr
{

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

const std::string ConsoleSpecRunner::kMenuString = 		
									"command         [abbreviation]\n"
									"-------         ---\n"
									"run all         [ra]\n"
									"run quick       [rq]\n"
									"run new         [rn]\n"
									"run selected    [rs]\n"
									"run failing     [rf]\n"

									"list all        [la]\n"
									"list quick      [lq]\n"
									"list new        [ln]\n"
									"list selected   [ls]\n"
									"list failing    [lf]\n"

									"summary         [sum]\n"

									"quit            [q]\n"
									"exit            [ex]\n"

									"select <name>   [sel]\n"
									"select <name> - <name>\n"
									"select <name> , <name> , ...\n"
									"clear           [cl]\n";

const std::string ConsoleSpecRunner::kRunCmd							= "run";
const std::string ConsoleSpecRunner::kListCmd						= "list";

const std::string ConsoleSpecRunner::kRunAllAbbrev					= "ra";
const std::string ConsoleSpecRunner::kRunQuickOnlyAbbrev			= "rq";
const std::string ConsoleSpecRunner::kRunNewFirstAbbrev			= "rn";
const std::string ConsoleSpecRunner::kRunFailingFirstAbbrev		= "rf";
const std::string ConsoleSpecRunner::kRunSelectedAbbrev			= "rs";

const std::string ConsoleSpecRunner::kListAllAbbrev				= "la";
const std::string ConsoleSpecRunner::kListQuickAbbrev				= "lq";
const std::string ConsoleSpecRunner::kListNewAbbrev				= "ln";
const std::string ConsoleSpecRunner::kListFailingAbbrev			= "lf";
const std::string ConsoleSpecRunner::kListSelectedAbbrev			= "ls";

const std::string ConsoleSpecRunner::kSummaryCmd			= "summary";
const std::string ConsoleSpecRunner::kSummaryAbbrev		= "sum";

const std::string ConsoleSpecRunner::kQuitCmd				= "quit";
const std::string ConsoleSpecRunner::kQuitAbbrev			= "q";
const std::string ConsoleSpecRunner::kExitCmd				= "exit";
const std::string ConsoleSpecRunner::kExitAbbrev			= "ex";

const std::string ConsoleSpecRunner::kSelectCmd				= "select";
const std::string ConsoleSpecRunner::kSelectAbbrev			= "sel";

const std::string ConsoleSpecRunner::kClearCmd				= "clear";
const std::string ConsoleSpecRunner::kClearAbbrev			= "cl";

const std::string ConsoleSpecRunner::nameTitle				= "Name";
const std::string ConsoleSpecRunner::resultTitle			= "Result";
const std::string ConsoleSpecRunner::hasBeenRunTitle		= "HasBeenRun";
const std::string ConsoleSpecRunner::durationTitle			= "Duration";

const int ConsoleSpecRunner::kBetweenCols = 2;

const int ConsoleSpecRunner::resultWidth = ConsoleSpecRunner::resultTitle.length() 
				+ ConsoleSpecRunner::kBetweenCols;

const int ConsoleSpecRunner::hasBeenRunWidth = 
	ConsoleSpecRunner::hasBeenRunTitle.length() + ConsoleSpecRunner::kBetweenCols;

const int ConsoleSpecRunner::durationWidth = 
	ConsoleSpecRunner::durationTitle.length() + ConsoleSpecRunner::kBetweenCols;

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

ConsoleCommand::ConsoleCommand( ConsoleSpecRunner* runnerIn )
	: runner( runnerIn )
{
}

ConsoleCommand::~ConsoleCommand()
{
}

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

class RunAllConsoleCommand : public ConsoleCommand
{
public:
	explicit RunAllConsoleCommand( ConsoleSpecRunner* runnerIn )
		: ConsoleCommand( runnerIn )
	{	};
	
	virtual void execute( SpecListener* listener, 
							const StringList& strList,
							std::ostream& outStream )
	{
		#pragma unused(strList)

		runner->runAll( listener );
		outStream << endl;
		runner->printSummary( outStream );
	};
};

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

class RunQuickOnlyConsoleCommand : public ConsoleCommand
{
public:
	explicit RunQuickOnlyConsoleCommand( ConsoleSpecRunner* runnerIn )
		: ConsoleCommand( runnerIn )
	{	};
	
	virtual void execute( SpecListener* listener, 
							const StringList& strList,
							std::ostream& outStream )
	{
		#pragma unused(strList)

		runner->runFastOnly( listener );
		outStream << endl;
		runner->printSummary( outStream );
	};
};

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

class RunSelectedOnlyConsoleCommand : public ConsoleCommand
{
public:
	explicit RunSelectedOnlyConsoleCommand( ConsoleSpecRunner* runnerIn )
		: ConsoleCommand( runnerIn )
	{	};
	
	virtual void execute( SpecListener* listener, 
							const StringList& strList,
							std::ostream& outStream )
	{
		#pragma unused(strList)

		runner->runSelectedOnly( listener );
		outStream << endl;
		runner->printSummary( outStream );
	};
};

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

class RunNewFirstConsoleCommand : public ConsoleCommand
{
public:
	explicit RunNewFirstConsoleCommand( ConsoleSpecRunner* runnerIn )
		: ConsoleCommand( runnerIn )
	{	};
	
	virtual void execute( SpecListener* listener, 
							const StringList& strList,
							std::ostream& outStream )
	{
		#pragma unused(strList)

		runner->runNewFirst( listener );
		outStream << endl;
		runner->printSummary( outStream );
	};
};

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

class RunFailingFirstConsoleCommand : public ConsoleCommand
{
public:
	explicit RunFailingFirstConsoleCommand( ConsoleSpecRunner* runnerIn )
		: ConsoleCommand( runnerIn )
	{	};
	
	virtual void execute( SpecListener* listener, 
							const StringList& strList,
							std::ostream& outStream )
	{
		#pragma unused(strList)

		runner->runFailingFirst( listener );
		outStream << endl;
		runner->printSummary( outStream );
	};
};

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

class ListAllConsoleCommand : public ConsoleCommand
{
public:
	explicit ListAllConsoleCommand( ConsoleSpecRunner* runnerIn )
		: ConsoleCommand( runnerIn )
	{	};

	virtual void execute( SpecListener* listener, 
							const StringList& strList,
							std::ostream& outStream )
	{
		#pragma unused(listener)
		#pragma unused(strList)

		runner->printAllSpecs( outStream );
	};
};

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

class ListFailingOnlyConsoleCommand : public ConsoleCommand
{
public:
	explicit ListFailingOnlyConsoleCommand( ConsoleSpecRunner* runnerIn )
		: ConsoleCommand( runnerIn )
	{	};

	virtual void execute( SpecListener* listener, 
							const StringList& strList,
							std::ostream& outStream )
	{
		#pragma unused(listener)
		#pragma unused(strList)

		runner->printFailingSpecs( outStream );
	};
};

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

class ListQuickOnlyConsoleCommand : public ConsoleCommand
{
public:
	explicit ListQuickOnlyConsoleCommand( ConsoleSpecRunner* runnerIn )
		: ConsoleCommand( runnerIn )
	{	};

	virtual void execute( SpecListener* listener, 
							const StringList& strList,
							std::ostream& outStream )
	{
		#pragma unused(listener)
		#pragma unused(strList)

		runner->printFastSpecs( outStream );
	};
};

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

class ListNewOnlyConsoleCommand : public ConsoleCommand
{
public:
	explicit ListNewOnlyConsoleCommand( ConsoleSpecRunner* runnerIn )
		: ConsoleCommand( runnerIn )
	{	};

	virtual void execute( SpecListener* listener, 
							const StringList& strList,
							std::ostream& outStream )
	{
		#pragma unused(listener)
		#pragma unused(strList)

		runner->printNewSpecs( outStream );
	};
};

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

class ListSelectedOnlyConsoleCommand : public ConsoleCommand
{
public:
	explicit ListSelectedOnlyConsoleCommand( ConsoleSpecRunner* runnerIn )
		: ConsoleCommand( runnerIn )
	{	};

	virtual void execute( SpecListener* listener, 
							const StringList& strList,
							std::ostream& outStream )
	{
		#pragma unused(listener)
		#pragma unused(strList)

		runner->printSelectedSpecs( outStream );
	};
};

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

class SummaryConsoleCommand : public ConsoleCommand
{
public:
	explicit SummaryConsoleCommand( ConsoleSpecRunner* runnerIn )
		: ConsoleCommand( runnerIn )
	{	};

	virtual void execute( SpecListener* listener, 
							const StringList& strList,
							std::ostream& outStream )
	{
		#pragma unused(listener)
		#pragma unused(strList)

		runner->printSummary( outStream );
	};
};

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

class QuitConsoleCommand : public ConsoleCommand
{
public:
	explicit QuitConsoleCommand( ConsoleSpecRunner* runnerIn )
		: ConsoleCommand( runnerIn )
	{	};

	virtual void execute( SpecListener* listener, 
							const StringList& strList,
							std::ostream& outStream )
	{
		#pragma unused(listener)
		#pragma unused(outStream)
		#pragma unused(strList)

		runner->setIsQuitting( true );
	};
};

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

class ClearConsoleCommand : public ConsoleCommand
{
public:
	explicit ClearConsoleCommand( ConsoleSpecRunner* runnerIn )
		: ConsoleCommand( runnerIn )
	{	};

	virtual void execute( SpecListener* listener, 
							const StringList& strList,
							std::ostream& outStream )
	{
		#pragma unused(listener)
		#pragma unused(outStream)
		#pragma unused(strList)

		runner->clearSelection();
	};
};

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


class SelectConsoleCommand : public ConsoleCommand
{
public:
	explicit SelectConsoleCommand( ConsoleSpecRunner* runnerIn )
		: ConsoleCommand( runnerIn )
	{	};

	virtual void execute( SpecListener* listener, 
							const StringList& strList,
							std::ostream& outStream )
	{
		#pragma unused(listener)
		#pragma unused(outStream)
		this->processSelectCommand( strList );
	};

	virtual void processSelectCommand( const StringList& strList )
	{
		if ( strList.size() == 2 and strList.at(0) == "select" )
		{
			runner->selectOneName( strList.at(1) );
		}
		else if ( strList.size() == 4 and strList.at(0) == "select" 
			and strList.at(2) == "-" )
		{
			runner->selectNameRange( strList.at(1), strList.at(3) );
		}
		else if ( strList.size() >= 4 and strList.at(0) == "select" 
			and strList.at(2) == "," )
		{
			runner->selectNameCommaList( strList, 1 );
		}
	}
};

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

class RunConsoleCommand : public ConsoleCommand
{
		ConsoleCommandMap mySubCommands;

public:
	explicit RunConsoleCommand( ConsoleSpecRunner* runnerIn )
		: ConsoleCommand( runnerIn )
	{
		mySubCommands.insert( std::make_pair( kAllCmd, 
			new RunAllConsoleCommand( runnerIn ) ) );

		mySubCommands.insert( std::make_pair( kQuickOnlyCmd, 
			new RunQuickOnlyConsoleCommand( runnerIn ) ) );

		mySubCommands.insert( std::make_pair( kNewFirstCmd, 
			new RunNewFirstConsoleCommand( runnerIn ) ) );

		mySubCommands.insert( std::make_pair( kSelectedCmd, 
			new RunSelectedOnlyConsoleCommand( runnerIn ) ) );

		mySubCommands.insert( std::make_pair( kFailingCmd, 
			new RunFailingFirstConsoleCommand( runnerIn ) ) );
	};
	
	virtual void execute( SpecListener* listener, 
							const StringList& strList,
							std::ostream& outStream )
	{
		if ( strList.size() == 2 )
		{
			std::string subCommand = strList.at(1);
			ConsoleCommandMap::iterator found = mySubCommands.find( subCommand );

			if ( found != mySubCommands.end() )
			{
				mySubCommands[ subCommand ]->execute( listener, strList, 
					outStream );
			}
		}
	};
};

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

class ListConsoleCommand : public ConsoleCommand
{
		ConsoleCommandMap mySubCommands;

public:
	explicit ListConsoleCommand( ConsoleSpecRunner* runnerIn )
		: ConsoleCommand( runnerIn )
	{
		mySubCommands.insert( std::make_pair( kAllCmd, 
			new ListAllConsoleCommand( runnerIn ) ) );

		mySubCommands.insert( std::make_pair( kQuickOnlyCmd, 
			new ListQuickOnlyConsoleCommand( runnerIn ) ) );

		mySubCommands.insert( std::make_pair( kNewFirstCmd, 
			new ListNewOnlyConsoleCommand( runnerIn ) ) );

		mySubCommands.insert( std::make_pair( kSelectedCmd, 
			new ListSelectedOnlyConsoleCommand( runnerIn ) ) );

		mySubCommands.insert( std::make_pair( kFailingCmd, 
			new ListFailingOnlyConsoleCommand( runnerIn ) ) );
	};
	
	virtual void execute( SpecListener* listener, 
							const StringList& strList,
							std::ostream& outStream )
	{
		if ( strList.size() == 2 )
		{
			std::string subCommand = strList.at(1);
			ConsoleCommandMap::iterator found = mySubCommands.find( subCommand );

			if ( found != mySubCommands.end() )
			{
				mySubCommands[ subCommand ]->execute( listener, strList, 
					outStream );
			}
		}
	};
};

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

ConsoleSpecRunner::ConsoleSpecRunner(const SpecRunner& rhs)
	: SpecRunner( rhs )
	, myIsQuitting( false )
{
}

ConsoleSpecRunner::ConsoleSpecRunner()
	: myIsQuitting( false )
{
	myCommands.insert( std::make_pair( kRunCmd, 
			new RunConsoleCommand( this ) ) );


	myCommands.insert( std::make_pair( kRunAllAbbrev, 
			new RunAllConsoleCommand( this ) ) );

	myCommands.insert( std::make_pair( kRunQuickOnlyAbbrev, 
			new RunQuickOnlyConsoleCommand( this ) ) );

	myCommands.insert( std::make_pair( kRunNewFirstAbbrev, 
			new RunNewFirstConsoleCommand( this ) ) );

	myCommands.insert( std::make_pair( kRunFailingFirstAbbrev, 
			new RunFailingFirstConsoleCommand( this ) ) );

	myCommands.insert( std::make_pair( kRunSelectedAbbrev, 
			new RunSelectedOnlyConsoleCommand( this ) ) );


	myCommands.insert( std::make_pair( kListCmd, 
			new ListConsoleCommand( this ) ) );


	myCommands.insert( std::make_pair( kListAllAbbrev, 
			new ListAllConsoleCommand( this ) ) );

	myCommands.insert( std::make_pair( kListFailingAbbrev, 
			new ListFailingOnlyConsoleCommand( this ) ) );

	myCommands.insert( std::make_pair( kListQuickAbbrev, 
			new ListQuickOnlyConsoleCommand( this ) ) );

	myCommands.insert( std::make_pair( kListNewAbbrev, 
			new ListNewOnlyConsoleCommand( this ) ) );


	myCommands.insert( std::make_pair( kSummaryCmd,
			new SummaryConsoleCommand( this ) ) );

	myCommands.insert( std::make_pair( kSummaryAbbrev,
			new SummaryConsoleCommand( this ) ) );


	myCommands.insert( std::make_pair( kQuitCmd,
			new QuitConsoleCommand( this ) ) );

	myCommands.insert( std::make_pair( kQuitAbbrev,
			new QuitConsoleCommand( this ) ) );

	myCommands.insert( std::make_pair( kExitCmd,
			new QuitConsoleCommand( this ) ) );

	myCommands.insert( std::make_pair( kExitAbbrev,
			new QuitConsoleCommand( this ) ) );


	myCommands.insert( std::make_pair( kClearCmd,
			new ClearConsoleCommand( this ) ) );

	myCommands.insert( std::make_pair( kClearAbbrev,
			new ClearConsoleCommand( this ) ) );


	myCommands.insert( std::make_pair( kSelectCmd,
			new SelectConsoleCommand( this ) ) );

	myCommands.insert( std::make_pair( kSelectAbbrev,
			new SelectConsoleCommand( this ) ) );
}

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

ConsoleSpecRunner::~ConsoleSpecRunner()
{
}

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

void ConsoleSpecRunner::outputMenu( std::ostream& outStream )
{
	outStream << kMenuString;
}

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

void ConsoleSpecRunner::printSummary( std::ostream& outStream )
{
	int numPass = this->numSpecsPassing();
	int numFail = this->numSpecsFailing();

	if ( numFail == 0 )
	{
		outStream << "SUCCESS; ";
	}
	else
	{
		outStream << "FAILED; ";
	}
	outStream << (numPass + numFail) 
		<< " ran; "
		<< numFail 
		<< " failed; " 
		<< this->totalSpecDuration()
		<< " seconds" 
		<< endl;
}

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

void ConsoleSpecRunner::printAllSpecs( std::ostream& outStream )
{
	printSpecs( this->allSpecs(), outStream );
}

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

void ConsoleSpecRunner::printFailingSpecs( std::ostream& outStream )
{
	printSpecs( this->failingSpecs(), outStream );
}

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

void ConsoleSpecRunner::printFastSpecs( std::ostream& outStream )
{
	printSpecs( this->fastSpecs(), outStream );
}

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

void ConsoleSpecRunner::printNewSpecs( std::ostream& outStream )
{
	printSpecs( this->newSpecs(), outStream );
}

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

void ConsoleSpecRunner::printSelectedSpecs( std::ostream& outStream )
{
	printSpecs( this->selectedContexts(), outStream );
}

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

int ConsoleSpecRunner::computeNameWidth( const ContextList& specs )
{
	int nameWidth = nameTitle.length();

	for ( ContextList::const_iterator i = specs.begin(); i != specs.end(); 
		++i )
	{
		Context* aContext = (*i);
		if ( aContext->name().length() > nameWidth )
			nameWidth = aContext->name().length();
	}
	nameWidth += kBetweenCols;
	return nameWidth;
}

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

void ConsoleSpecRunner::printSpecColumnTitles( std::ostream& outStream,
		int nameWidth )
{
	// output the titles with spaces appended

	outStream << padWithSpaces( nameTitle, nameWidth );
	outStream << padWithSpaces( resultTitle, resultWidth );
	outStream << padWithSpaces( hasBeenRunTitle, hasBeenRunWidth );
	outStream << padWithSpaces( durationTitle, durationWidth );
	outStream << endl;

	// output separators
	
	outStream << padWithSpaces( padWithChar( "", nameTitle.length(), '-' ), 
		nameWidth );
	outStream << padWithSpaces( padWithChar( "", resultTitle.length(), '-' ),
		resultWidth );
	outStream << padWithSpaces( padWithChar( "", hasBeenRunTitle.length(), '-' ),
		hasBeenRunWidth );
	outStream << padWithSpaces( padWithChar( "", durationTitle.length(), '-' ),
		durationWidth );
	outStream << endl;
}

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

void ConsoleSpecRunner::printSpecRow( const Context*		aContext, 
											std::ostream&		outStream, 
											int					nameWidth )
{
	outStream << padWithSpaces( aContext->name(), nameWidth );
	outStream << padWithSpaces( failString( aContext->hasFailed() ), 
		resultWidth );
	outStream << padWithSpaces( runString( aContext->hasBeenRun() ), hasBeenRunWidth );
	outStream << padWithSpaces( durationString( aContext->duration() ),
		durationWidth );
	outStream << endl;
}

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

void ConsoleSpecRunner::printSpecs( const ContextList& specs, 
			std::ostream& outStream )
{	
	// the name column is the only one that will need to be resized
	// to deal with long names.

	int nameWidth = computeNameWidth( specs );

	printSpecColumnTitles( outStream, nameWidth );

	for ( ContextList::const_iterator i = specs.begin(); i != specs.end(); ++i )
	{
		Context* aContext = (*i);
		printSpecRow( aContext, outStream, nameWidth );
	}
	outStream << endl;
}

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

void ConsoleSpecRunner::processInput( std::ostream& outStream, 
		std::istream& inStream )
{
	if ( inStream.eof() )
	{
		setIsQuitting( true );
		return;
	}

	std::string aLine;
	std::getline( inStream, aLine, '\n' );

	if ( aLine.length() == 0 )
	{
		return; // don't process empty lines
	}

	CmdTokenizer tokenizer( aLine );
	StringList strList = tokenizer.tokenizeCmdString();
	
	if ( strList.size() == 0 )
	{
		return; // don't process empty lines
	}

	StdOutStreamSpecListener listener( outStream );

	std::string command = strList.at(0);
	ConsoleCommandMap::iterator found = myCommands.find( command );

	if ( found != myCommands.end() )
	{
		myCommands[ command ]->execute( & listener, strList, outStream );
		std::string failures = listener.logStr();
		outStream << failures << endl;
	}
	else
	{
		outputMenu( outStream );
	}
}

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

bool ConsoleSpecRunner::isQuitting() const
{
	return myIsQuitting;
}

void ConsoleSpecRunner::setIsQuitting( bool val )
{
	myIsQuitting = val;
}

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

ContextList ConsoleSpecRunner::selectedContexts() const
{
	return mySelectedContexts;
}

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

void ConsoleSpecRunner::selectNameCommaList( const StringList& strList,
		int startIndex )
{
	this->selectOneName( strList.at( startIndex ) );
	// strList.at(startIndex + 1) == ","
	startIndex = startIndex + 2;
	
	if ( startIndex < strList.size() )
	{
		this->selectNameCommaList( strList, startIndex );
	}
}

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

void ConsoleSpecRunner::selectOneName( const std::string& contextNameIn )
{
	std::string upperName = toUpper( contextNameIn );
	for ( ContextList::const_iterator i = myContextList.begin(); i != myContextList.end(); ++i )
	{
		if ( toUpper( (*i)->name() ) == upperName )
		{
			mySelectedContexts.push_back( *i );
		}
	}
}

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

void ConsoleSpecRunner::selectNameRange( const std::string& firstName,
					const std::string& secondName )
{
	std::string upperFirstName = toUpper( firstName );
	std::string upperSecondName = toUpper( secondName );
	
	if ( upperFirstName > upperSecondName )
	{
		std::swap( upperFirstName, upperSecondName );
	}
	ContextList contextsInOrder = this->allSpecs();
	
	for ( ContextList::iterator i = contextsInOrder.begin(); 
			i != contextsInOrder.end(); ++i )
	{
		std::string ithName = toUpper( (*i)->name() );
		if ( ithName >= upperFirstName 
			and ithName <= upperSecondName )
		{
			mySelectedContexts.push_back( *i );
		}
	}
}

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

void ConsoleSpecRunner::clearSelection()
{
	mySelectedContexts.clear();
}

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

void ConsoleSpecRunner::runSelectedOnly( SpecListener* listenerIn )
{
	for ( ContextList::iterator i = mySelectedContexts.begin(); 
			i != mySelectedContexts.end(); ++i )
	{
		this->runOne( *i, listenerIn );
	}
}

//  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

}; // namespace

